const os = require('os');
const path = require('path');
const electron = require("electron");
const puppeteer = require("puppeteer-core");
const spawn = require("child_process").spawn;

const userData = path.join(os.tmpdir(), 'hakuneko-test', Date.now().toString(32));
const command = {
    app: electron,
    args: [
        '.',
        '--update-url=DISABLED',
        '--cache-directory=' + path.join('src', 'web'),
        '--user-directory=' + userData,
        '--remote-debugging-port=9200'
    ],
    opts: {
        shell: false
    }
};
const connection = {
    browserURL: "http://localhost:9200",
    defaultViewport: null
};

async function assertConnector(browserPage, parameters, expectations) {

    // get connector for website as reference to the remote instance
    let remoteConnector = await browserPage.evaluateHandle(connectorID => {
        return Engine.Connectors.find(connector => connector.id === connectorID);
    }, parameters.connectorID);
    expect(await browserPage.evaluate(o => o.constructor.name, remoteConnector)).toEqual(expectations.connectorClass);

    // get manga for website as reference to the remote instance
    let remoteManga = await browserPage.evaluateHandle((connector, mangaURL) => {
        return connector.getMangaFromURI(new URL(mangaURL));
    }, remoteConnector, parameters.mangaURL);
    expect(await browserPage.evaluate(o => o.constructor.name, remoteManga)).toEqual('Manga');
    let remoteMangaID = await (await remoteManga.getProperty('id')).jsonValue();
    let remoteMangaTitle = await (await remoteManga.getProperty('title')).jsonValue();
    expect(remoteMangaID).toEqual(expectations.mangaID);
    expect(remoteMangaTitle).toEqual(expectations.mangaTitle);

    // get chapters for manga as reference to the remote instance
    let remoteChapters = await browserPage.evaluateHandle(manga => {
        return new Promise(resolve => {
            manga.getChapters((_, chapters) => resolve(chapters));
        });
    }, remoteManga);
    expect(remoteChapters._remoteObject.className).toEqual('Array');

    // get first chapter for chapters as reference to the remote instance
    let remoteChapter = await browserPage.evaluateHandle((chapters, chaptersAccessor) => {
        // first => shift, last => pop, INT => getAtIndex
        return Number.isInteger(chaptersAccessor) ? chapters[chaptersAccessor] : chapters[chaptersAccessor]();
    }, remoteChapters, parameters.chaptersAccessor);
    expect(await browserPage.evaluate(o => o.constructor.name, remoteChapter)).toEqual('Chapter');
    let remoteChapterID = await (await remoteChapter.getProperty('id')).jsonValue();
    let remoteChapterTitle = await (await remoteChapter.getProperty('title')).jsonValue();
    expect(remoteChapterID).toEqual(expectations.chapterID);
    expect(remoteChapterTitle).toEqual(expectations.chapterTitle);

    // get pages for chapter as reference to the remote instance
    let pages = await browserPage.evaluate(chapter => {
        return new Promise(resolve => {
            chapter.getPages((_, pages) => resolve(pages));
        });
    }, remoteChapter);
    expect(pages.length).toEqual(expectations.pageCount);
    pages = pages.map(page => {
        if (page.startsWith('connector://' + parameters.connectorID)) {
            let payload = decodeURIComponent(page.split('payload=')[1]);
            payload = JSON.parse(Buffer.from(payload, 'base64').toString());
            if(parameters.connectorID == 'mangadex') {
                return payload.networkNode + payload.hash + '/' + payload.file;
            }
            return payload.url;
        } else {
            return page;
        }
    });
    if (expectations.pageMatcher) {
        pages.forEach(page => expect(page).toMatch(expectations.pageMatcher));
    }
}

describe("HakuNeko Engine", () => {

    jest.setTimeout(50000);

    var process;
    var browser;
    var page;

    // SetUp
    beforeAll(async () => {
        //jest.clearAllMocks();
        process = spawn(command.app, command.args, command.opts);
        process.stdout.on('data', (data) => {
            console.log(`hakuneko [stdout]: ${data}`);
        });
        process.stderr.on('data', (data) => {
            console.error(`hakuneko [stderr]: ${data}`);
        });
        await new Promise(resolve => setTimeout(resolve, 7500));
        browser = await puppeteer.connect(connection);
        [page] = await browser.pages();
    });

    // TearDown
    afterAll(async () => {
        try {
            await page.close();
            await browser.close();
        } catch (error) {
            await new Promise(resolve => setTimeout(resolve, 5000));
            process.kill();
        }
    });

    beforeEach(async () => {
        // TODO: cleanup user data directory?
        await page.reload();
        await page.waitForSelector('hakuneko-app');
    });

    describe('Connectors', () => {

        describe('Clipboard', () => {
            //
        });

        describe('MangaDex', () => {
            it('should get manga, chapters and page links', async () => {
                await assertConnector(page, {
                    connectorID: 'mangadex',
                    mangaURL: 'https://mangadex.org/title/0e711546-48be-4d95-90eb-c336cfc0ddce',
                    chaptersAccessor: 'pop' // first => shift, last => pop, index => Integer
                }, {
                    connectorClass: 'MangaDex',
                    mangaID: '0e711546-48be-4d95-90eb-c336cfc0ddce',
                    mangaTitle: 'They Say I Was Born a King\'s Daughter',
                    chapterID: '87623776-1db5-458e-b057-a4faff9d4af1',
                    chapterTitle: 'Ch.0001 (pt-br) [Usagi Scan]',
                    pageCount: 51,
                    pageMatcher: /^https:\/\/(s\d+|uploads).mangadex.org\/data\/[0-9a-f]{32}\/R\d+-[0-9a-f]{64}.jpg$/
                });
            });
        });

        describe('MangaGo', () => {
            it('should get manga, chapters and page links', async () => {
                await assertConnector(page, {
                    connectorID: 'mangago',
                    mangaURL: 'http://www.mangago.me/read-manga/black_clover/',
                    chaptersAccessor: 'pop' // first => shift, last => pop, index => Integer
                }, {
                    connectorClass: 'MangaGo',
                    mangaID: '/read-manga/black_clover/',
                    mangaTitle: 'Black Clover',
                    chapterID: '/read-manga/black_clover/bt/314637/Ch1/',
                    chapterTitle: 'Ch.1  : The Boy\'s Vow',
                    pageCount: 51,
                    pageMatcher: /^https?:\/\/iweb\d+\.mangapicgallery.com\/r\/newpiclink\/black_clover\/1\/[a-z0-9]{32}\.(?:png|jpg|jpeg)$/
                });
            });
        });

        describe('EpikManga', () => {
            it('should get manga, chapters and page links', async () => {
                await assertConnector(page, {
                    connectorID: 'epikmanga',
                    mangaURL: 'https://www.epikmanga.com/seri/battle-through-the-heavens',
                    chaptersAccessor: 'pop' // first => shift, last => pop, index => Integer
                }, {
                    connectorClass: 'EpikManga',
                    mangaID: '/seri/battle-through-the-heavens',
                    mangaTitle: 'Battle Through the Heavens',
                    chapterID: '/seri/battle-through-the-heavens/bolum/6',
                    chapterTitle: '#1 Artık Dahi Değil',
                    pageCount: 20,
                    pageMatcher: /^https:\/\/www\.epikmanga.com\/upload\/manga\/battle-through-the-heavens\/1\/\d+.jpg$/
                });
            });
        });
        describe('Baozimh', () => {
            it('should get manga, chapters and page links', async () => {
                await assertConnector(page, {
                    connectorID: 'baozimh',
                    mangaURL: 'https://www.baozimh.com/comic/nudiduolanyan-fuxiaolianmeng',
                    chaptersAccessor: 'pop' // first => shift, last => pop, index => Integer
                }, {
                    connectorClass: 'Baozimh',
                    mangaID: '/comic/nudiduolanyan-fuxiaolianmeng',
                    mangaTitle: '女帝多藍顏',
                    chapterID: '/user/page_direct?comic_id=nudiduolanyan-fuxiaolianmeng&section_slot=0&chapter_slot=0',
                    chapterTitle: '預告',
                    pageCount: 46,
                    pageMatcher: /^https:\/\/.*.baozimh.com\/scomic\/nudiduolanyan-fuxiaolianmeng\/\d\/.*\/(\d*).jpg$/
                });
            });
        });
        describe('ComicBrise', () => {
            it('should get manga, chapters and page links', async () => {
                await assertConnector(page, {
                    connectorID: 'comicbrise',
                    mangaURL: 'https://comic-brise.com/contents/oshiai',
                    chaptersAccessor: 'pop' // first => shift, last => pop, index => Integer
                }, {
                    connectorClass: 'ComicBrise',
                    mangaID: '/contents/oshiai',
                    mangaTitle: '推しに認知してもらうためにアイドル始めました。',
                    chapterID: '/comic_ep/oshiai_ep1',
                    chapterTitle: '第１話',
                    pageCount: 49
                });
            });
        });
        describe('To-Corona-Ex', () => {
            it('should get manga, chapters and page links', async () => {
                await assertConnector(page, {
                    connectorID: 'to-corona-ex',
                    mangaURL: 'https://to-corona-ex.com/comics/20000000051530',
                    chaptersAccessor: 'pop' // first => shift, last => pop, index => Integer
                }, {
                    connectorClass: 'ToCoronaEx',
                    mangaID: '20000000051530',
                    mangaTitle: '悪役令嬢ですが攻略対象の様子が異常すぎる@COMIC',
                    chapterID: "20000000496345",
                    chapterTitle: '第1話',
                    pageCount: 34
                });
            });
        });
        describe('ReadM', () => {
            it('should get manga, chapters and page links', async () => {
                await assertConnector(page, {
                    connectorID: 'readm',
                    mangaURL: 'https://readm.org/manga/9465',
                    chaptersAccessor: 'pop' // first => shift, last => pop, index => Integer
                }, {
                    connectorClass: 'ReadM',
                    mangaID: '/manga/9465',
                    mangaTitle: 'D.N. Angel',
                    chapterID: '/manga/9465/1/all-pages',
                    chapterTitle: 'Chapter 1',
                    pageCount: 59,
                    pageMatcher: /^https:\/\/readm\.org\/uploads\/chapter_files\/9465\/0\/p_\d{5}\.jpg\?v=\d+$/
                });
            });
        });
        describe('Comic Valkyrie', () => {
            it('should get manga, chapters and page links', async () => {
                await assertConnector(page, {
                    connectorID: 'comicvalkyrie',
                    mangaURL: 'https://www.comic-valkyrie.com/isemaji',
                    chaptersAccessor: 'pop' // first => shift, last => pop, index => Integer
                }, {
                    connectorClass: 'ComicValkyrie',
                    mangaID: 'isemaji',
                    mangaTitle: '異世界魔術師は魔法を唱えない',
                    chapterID: 'https://www.comic-valkyrie.com/samplebook/val_isemaji01/',
                    chapterTitle: '第1話',
                    pageCount: 34,
                });
            });
        });
        describe('Futekiya', () => {
            it('should get manga, chapters and page links', async () => {
                await assertConnector(page, {
                    connectorID: 'futekiya',
                    mangaURL: 'https://read.futekiya.com/comic/5d13135038bcc',
                    chaptersAccessor: 'shift' // first => shift, last => pop, index => Integer
                }, {
                    connectorClass: 'Futekiya',
                    mangaID: '/comic/5d13135038bcc',
                    mangaTitle: 'Mood Indigo',
                    chapterID: "https://image.futekiya.com/viewer/5d13137ec6b1c",
                    chapterTitle: 'Volume 1 - Chapter 1 Deeper than Blue',
                    pageCount: 29
                });
            });
        });
    });
});